package com.zhuhjay.app.web.controller.wxmp;

import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import com.zhuhjay.common.base.annotation.ApiLimiter;
import com.zhuhjay.common.base.exception.BasicBusinessException;
import com.zhuhjay.common.redis.util.RedisUtils;
import com.zhuhjay.common.security.context.LoginUserContext;
import com.zhuhjay.common.web.annotation.NotHttpResult;
import com.zhuhjay.framework.security.service.LoginService;
import com.zhuhjay.service.system.enums.OauthTypeEnum;
import com.zhuhjay.service.system.model.entity.SystemOauth;
import com.zhuhjay.service.system.model.entity.SystemUser;
import com.zhuhjay.service.system.service.SystemOauthService;
import com.zhuhjay.service.system.service.SystemUserService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.validation.constraints.NotBlank;

import static com.zhuhjay.common.base.constant.CacheConstant.*;
import static com.zhuhjay.common.base.constant.TimeConstant.MINUTE;

/**
 * 微信相关授权接口，也属于公共业务接口
 *
 * @author ZhuHJay
 * @date 2023/8/21 17:10
 */
@Slf4j
@Api(tags = "微信相关授权接口")
@RestController
@RequestMapping("wx")
public class WxMpOauthController {

    @Resource
    private SystemUserService userService;
    @Resource
    private LoginService loginService;
    @Resource
    private SystemOauthService oauthService;

    @ApiLimiter(limit = 5, message = "请勿频繁操作")
    @ApiOperation("微信授权:获取业务授权码")
    @GetMapping("genera/oauth/code/{business}")
    public String generaOauthCode(@PathVariable @NotBlank(message = "错误的请求") String business) {
        String code, key, value;
        Integer userId = LoginUserContext.getUserId();
        switch (business) {
            case "login":
                code = getNotRepeatCode(WX_LOGIN_CODE_PREFIX);
                key = WX_LOGIN_CODE_PREFIX + code;
                value = "";
                break;
            case "bind":
                if (userId == null) {
                    throw new BasicBusinessException("请先登录");
                }
                if (oauthService.getOauthByUserId(userId, OauthTypeEnum.WECHAT) != null) {
                    throw new BasicBusinessException("该账号已绑定微信");
                }
                code = getNotRepeatCode(WX_BIND_CODE_PREFIX);
                key = WX_BIND_CODE_PREFIX + code;
                value = String.valueOf(userId);
                break;
            case "unbind":
                if (userId == null) {
                    throw new BasicBusinessException("请先登录");
                }
                SystemOauth oauth;
                if ((oauth = oauthService.getOauthByUserId(userId, OauthTypeEnum.WECHAT)) == null) {
                    throw new BasicBusinessException("该账号未绑定微信");
                }
                code = getNotRepeatCode(WX_UNBIND_CODE_PREFIX);
                key = WX_UNBIND_CODE_PREFIX + code;
                value = oauth.getOpenId();
                break;
            default:
                throw new BasicBusinessException("错误的请求");
        }
        RedisUtils.setCacheAndExpire(key, value, 5 * MINUTE);
        return code;
    }

    @ApiOperation("微信授权:取消业务授权码授权")
    @DeleteMapping("cancel/oauth/{business}/{code}")
    public void cancelOauthCode(
            @PathVariable @NotBlank(message = "错误的请求") String business,
            @PathVariable @NotBlank(message = "错误的请求") String code) {
        String key;
        switch (business) {
            case "login":
                key = WX_LOGIN_CODE_PREFIX + code;
                break;
            case "bind":
                key = WX_BIND_CODE_PREFIX + code;
                break;
            case "unbind":
                key = WX_UNBIND_CODE_PREFIX + code;
                break;
            default:
                throw new BasicBusinessException("错误的请求");
        }
        if (RedisUtils.hasKey(key)) {
            RedisUtils.deleteCache(key);
        }
    }

    @ApiOperation("微信授权:轮询是否业务授权")
    @GetMapping("polling/oauth/{business}/{code}")
    @NotHttpResult
    public String pollingOauth(
            @PathVariable @NotBlank(message = "错误的请求") String business,
            @PathVariable @NotBlank(message = "错误的请求") String code) {
        String key, cache;
        Integer userId = LoginUserContext.getUserId();
        switch (business) {
            case "login":
                key = WX_LOGIN_CODE_PREFIX + code;
                cache = RedisUtils.getCache(key);
                if (cache == null) {
                    return "error";
                }
                if ("".equals(cache)) {
                    return "continue";
                }
                userId = Integer.parseInt(cache);
                RedisUtils.deleteCache(key);
                SystemUser systemUser = userService.getById(userId);
                // 生成token, 返回给客户端
                loginService.authLogin(systemUser);
                // 记录用户登录时间
                userService.updateLastLoginTime(systemUser.getUsername());
                break;
            case "bind":
                if (userId == null) {
                    throw new BasicBusinessException("请先登录");
                }
                key = WX_BIND_CODE_PREFIX + code;
                cache = RedisUtils.getCache(key);
                if (cache == null) {
                    return "error";
                }
                if (StrUtil.isNotBlank(cache)) {
                    return "continue";
                }
                RedisUtils.deleteCache(key);
                break;
            case "unbind":
                if (userId == null) {
                    throw new BasicBusinessException("请先登录");
                }
                key = WX_UNBIND_CODE_PREFIX + code;
                cache = RedisUtils.getCache(key);
                if (cache == null) {
                    return "error";
                }
                if (StrUtil.isNotBlank(cache)) {
                    return "continue";
                }
                RedisUtils.deleteCache(key);
                break;
            default:
                return "error";
        }
        return "success";
    }

    /**
     * 获取不重复的授权码
     */
    private String getNotRepeatCode(String keyPrefix) {
        int maxRetry = 5;
        String code, key;
        do {
            code = IdUtil.nanoId(6);
            key = keyPrefix + code;
        } while (RedisUtils.hasKey(key) && --maxRetry > 0);
        // 尝试5次后还是失败，抛出异常
        if (maxRetry == 0) {
            throw new BasicBusinessException("获取授权码失败，请稍后重试");
        }
        return code;
    }

}
